#!/usr/bin/python3
"""Wait until pki-tomcatd is up

The script polls on Dogtag's HTTP port and wait until the admin interface
reports status 'running' for the CA sub system.

/etc/systemd/system/pki-tomcatd@pki-tomcat.service.d/ipa.conf
[Service]
ExecStartPost=/usr/libexec/ipa/ipa-pki-wait-running
"""
import os
import logging
import sys
import time
from xml.etree import ElementTree
import json

from ipalib import api
from ipaplatform.paths import paths

from pki.client import PKIConnection
from pki.system import SystemStatusClient
from requests.exceptions import ConnectionError, Timeout, RequestException

logger = logging.getLogger('ipa-pki-wait-running')

# check the CA subsystem. All pki-tomcatd instances in IPA have a CA
SUBSYSTEM = 'ca'
# time out for TCP connection attempts
CONNECTION_TIMEOUT = 1.0

EXIT_SUCCESS = 0
EXIT_FAILURE = 1


if hasattr(time, 'monotonic'):
    curtime = time.monotonic
else:
    curtime = time.time


def check_installed(subsystem):
    """Check if the subsystem is configured
    """
    catalina_base = os.environ.get(
        'CATALINA_BASE', '/var/lib/pki/pki-tomcat'
    )
    # /var/lib/pki/pki-tomcat/conf -> /etc/pki/pki-tomcat
    cs_cfg = os.path.join(catalina_base, 'conf', subsystem, 'CS.cfg')
    if os.path.isfile(cs_cfg):
        logger.debug("File %s exists.", cs_cfg)
        return True
    else:
        logger.info("File %s does not exist.", cs_cfg)
        return False


def get_client(hostname):
    """Create a connection object
    """
    conn = PKIConnection(
        hostname=hostname,
        cert_paths=paths.IPA_CA_CRT
    )
    client = SystemStatusClient(conn, subsystem=SUBSYSTEM)
    logger.info(
        "Created connection %s%s",
        client.connection.serverURI, client.get_status_url
    )
    return client


def get_status(client, timeout):
    """Get status from subsystem and return parsed (status, error)
    """
    response = client.get_status(timeout=timeout)
    status = None
    error = None
    try:
        json_response = json.loads(response)
        status = json_response['Response']['Status']
    except KeyError as e:
        error = repr(e)
    except json.JSONDecodeError:
        logger.debug("Response is not valid JSON, try XML")
        root = ElementTree.fromstring(response)
        status = root.findtext("Status")
        error = root.findtext("Error")
    logger.debug("Got status '%s', error '%s'", status, error)
    return status, error


def main():
    if not check_installed(SUBSYSTEM):
        logger.info(
            "subsystem %s is not installed, exiting", SUBSYSTEM
        )
        sys.exit(EXIT_SUCCESS)

    # bootstrap ipalib.api to parse config file
    api.bootstrap(confdir=paths.ETC_IPA, log=None)
    timeout = api.env.startup_timeout

    client = get_client(api.env.host)
    end = curtime() + timeout
    while curtime() < end:
        try:
            status, error = get_status(client, CONNECTION_TIMEOUT)
        except (ConnectionError, Timeout) as e:
            logger.info("Connection failed: %s", e)
        except RequestException as e:
            logger.error("Request failed unexpectedly, %s ", e)
        else:
            if status == 'running':
                logger.info("Success, subsystem %s is running!", SUBSYSTEM)
                sys.exit(EXIT_SUCCESS)
            elif error is not None:
                logger.info(
                    "Subsystem %s failed with error '%s', giving up!",
                    SUBSYSTEM, error
                )
                sys.exit(EXIT_FAILURE)
            else:
                logger.info("Status is '%s', waiting...", status)

        # wait and try again
        time.sleep(1)

    # giving up
    logger.error(
        "Reached end of wait timeout %s, giving up", timeout
    )
    sys.exit(EXIT_FAILURE)


if __name__ == '__main__':
    logging.basicConfig(
        format='%(name)s: %(message)s',
        level=logging.INFO
    )
    main()
